package edu.whut.framework.aop.util;

import edu.whut.framework.aop.interceptor.MyMethodInterceptor;
import edu.whut.framework.aop.annotation.Aspect;
import edu.whut.framework.aop.annotation.Order;
import edu.whut.framework.aop.service.AbstractAspectService;
import edu.whut.framework.aop.service.OrderAspectService;
import edu.whut.framework.container.BeanContainer;

import java.lang.annotation.Annotation;
import java.util.*;

/**
 * @program: MySpring
 * @description: 织入器
 * @author: Wayne
 * @create: 2020-08-07 10:41
 **/
public class Weaver {
    private BeanContainer beanContainer;
    private Map<Class<?>, List<OrderAspectService>> map;

    public Weaver() {
        this.beanContainer = BeanContainer.getInstance();
        map = new HashMap<>();
    }

    public void findJointPoint() {
        //获取所有切面类
        Set<Class<?>> aspectClassList = beanContainer.getClassesByAnnotation(Aspect.class);
        for (Class<?> aspectClass : aspectClassList) {
            if (checkAspectClass(aspectClass)) {
                Aspect aspect = aspectClass.getAnnotation(Aspect.class);
                //找出它需要织入的目标注解对象
                Class<? extends Annotation> targetAnnotation = aspect.value();
                //获取被这个注解标记的所有类
                Set<Class<?>> targetClassList = beanContainer.getClassesByAnnotation(targetAnnotation);
                for (Class<?> targetClass : targetClassList) {
                    storeAspectClass4TargetClass(targetClass, aspectClass);
                }
            }
        }
        doWeave();

    }

    private void doWeave() {
        for (Class<?> clazz :map.keySet()) {
            List<OrderAspectService> orderAspectServiceList = map.get(clazz);
            MyMethodInterceptor mm = new MyMethodInterceptor(clazz, orderAspectServiceList);
            Object proxy = CglibUtil.createProxy(clazz, mm);
            beanContainer.addBean(clazz, proxy);
        }
    }

    private boolean checkAspectClass(Class<?> aspectClass) {
        return  aspectClass.isAnnotationPresent(Aspect.class)&&
                aspectClass.isAnnotationPresent(Order.class)&&
                AbstractAspectService.class.isAssignableFrom(aspectClass) &&
                !aspectClass.getAnnotation(Aspect.class).value().equals(Aspect.class);
    }

    private void storeAspectClass4TargetClass(Class<?> targetClass, Class<?> aspectClass) {
        AbstractAspectService abstractAspectService = (AbstractAspectService)beanContainer.getBean(aspectClass);
        if (!map.containsKey(targetClass)) {
            List<OrderAspectService> serviceList = new ArrayList<>();
            serviceList.add(new OrderAspectService(abstractAspectService, aspectClass.getAnnotation(Order.class).value()));
            map.put(targetClass, serviceList);
        }
        else {
            map.get(targetClass).add(new OrderAspectService(abstractAspectService, aspectClass.getAnnotation(Order.class).value()));
        }
    }

}
